package com.yj.lottery_system.service.impl;

import com.yj.lottery_system.common.errorcode.ServiceErrorCodeConstants;
import com.yj.lottery_system.common.exception.ServiceException;
import com.yj.lottery_system.common.utils.JacksonUtil;
import com.yj.lottery_system.common.utils.RedisUtil;
import com.yj.lottery_system.controller.param.DrawPrizeParam;
import com.yj.lottery_system.controller.param.WinningRecordsParam;
import com.yj.lottery_system.dao.dataObject.*;
import com.yj.lottery_system.dao.mapper.*;
import com.yj.lottery_system.service.IDrawPrizeService;
import com.yj.lottery_system.service.dto.WinningRecordsDTO;
import com.yj.lottery_system.service.enums.ActivityPrizeStatusEnum;
import com.yj.lottery_system.service.enums.ActivityPrizeTiresEnum;
import com.yj.lottery_system.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.yj.lottery_system.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.yj.lottery_system.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements IDrawPrizeService {
    private final static Logger log = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
   @Autowired
    private RedisUtil redisUtil;
   //前缀
   private final static String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";
    //过期时间
    private final static Long WINNING_RECORDS_TIMEOUT = (long)(60*60*24*2);

    /**
     * 异步抽奖接口
     * @param param
     */
    @Override
    public void drawPrize(DrawPrizeParam param) {
        Map<String,String> map = new HashMap<>();
        map.put("messageId", String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        //发消息: 交换机、绑定key、消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING,map);
        log.info("mq消息发送成功： map: {}", JacksonUtil.writeValueAsString(map));

    }

    /**
     * 校验抽奖请求
     * @param param
     */
    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
        //活动是否存在
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        //奖品是否存在，从activity_prize表获取，因为使用了本地事务，保持一致性
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());

        //活动和奖品是否存在
        if(null == activityDO || null == activityPrizeDO) {
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            log.info("校验抽奖请求失败："+ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY.getMsg());
            return false;
        }

        //活动是否有效
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            log.info("校验抽奖请求失败："+ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getMsg());
            return false;
        }

        //奖品是否有效
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            log.info("校验抽奖请求失败："+ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED.getMsg());
            return false;
        }

        //中奖者人数是否与奖品数量一致
        if(activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
            //throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            log.info("校验抽奖请求失败："+ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR.getMsg());
            return false;
        }
        return true;
    }
    /**
     * 保存中奖者名单
     * @param param
     */
    @Override
    public List<WinningRecodesDO> saveWinnerRecodes(DrawPrizeParam param) {
        //查询活动 奖品 人员信息 活动关联奖品表
        //查询活动
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        //查询人员信息
        List<UserDO> userDOList = userMapper.batchSelectByIds(param.getWinnerList().stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList()
                ));
        //查询奖品
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        //查询 活动关联奖品表
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());

        //构造中奖者记录
        List<WinningRecodesDO> winningRecodesDOList = userDOList.stream()
                .map(userDO -> {
                    WinningRecodesDO winningRecodesDO = new WinningRecodesDO();
                    winningRecodesDO.setActivityId(activityDO.getId());
                    winningRecodesDO.setActivityName(activityDO.getActivityName());
                    winningRecodesDO.setPrizeId(prizeDO.getId());
                    winningRecodesDO.setPrizeName(prizeDO.getName());
                    winningRecodesDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecodesDO.setWinnerId(userDO.getId());
                    winningRecodesDO.setWinnerName(userDO.getUserName());
                    winningRecodesDO.setWinnerEmail(userDO.getEmail());
                    winningRecodesDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecodesDO.setWinningTime(param.getWinningTime());
                    return winningRecodesDO;
                }).collect(Collectors.toList());

        winningRecordMapper.batchInsert(winningRecodesDOList);

        //缓存中奖者记录
        //缓存奖品维度(activityId_prizeId_ , winningRecodesDOList)
        cacheWinningRecords(param.getActivityId()+"_"+param.getPrizeId(),
                winningRecodesDOList,
                WINNING_RECORDS_TIMEOUT);
        //活动已完成 缓存活动维度(activityId_ , winningRecodesDOList)
        if(activityDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            //查询活动维度全量中奖记录
           List<WinningRecodesDO> allList =  winningRecordMapper.selectByActivityId(param.getActivityId());
            cacheWinningRecords(String.valueOf(param.getActivityId()),
                    allList,
                    WINNING_RECORDS_TIMEOUT);
        }
        return winningRecodesDOList;
    }



    /**
     * 缓存中奖记录
     * @param key
     * @param winningRecodesDOList
     * @param time
     */

    private void cacheWinningRecords(String key, List<WinningRecodesDO> winningRecodesDOList, Long time) {
        try {
            if(!StringUtils.hasText(key)
                    || CollectionUtils.isEmpty(winningRecodesDOList)) {
                log.warn("要缓存的内容为空：key: {}, value: {}",
                        WINNING_RECORDS_PREFIX+key,
                        JacksonUtil.writeValueAsString(winningRecodesDOList));
                return;
            }
            redisUtil.set(WINNING_RECORDS_PREFIX+key,
                    JacksonUtil.writeValueAsString(winningRecodesDOList),
                    time);
        }catch (Exception e) {
            log.error("缓存中奖记录异常：key: {}, value: {}",
                    WINNING_RECORDS_PREFIX+key,
                    JacksonUtil.writeValueAsString(winningRecodesDOList));
        }
    }
    /**
     * 获取缓存中的中奖记录
     * @param key
     */

    private List<WinningRecodesDO> getWinningRecords(String key) {
        try{
            if(!StringUtils.hasText(key)) {
                log.warn("要查询的缓存中奖记录key为空：key: {}", WINNING_RECORDS_PREFIX+key);
                return Arrays.asList();
            }
            String s = redisUtil.get(WINNING_RECORDS_PREFIX + key);
            if (!StringUtils.hasText(s)) {
                return Arrays.asList();
            }
            List<WinningRecodesDO> winningRecodesDOList = JacksonUtil.readListValue(s, WinningRecodesDO.class);
            return winningRecodesDOList;
        }catch (Exception e) {
            log.error("要查询的缓存中奖记录异常：key: {}", WINNING_RECORDS_PREFIX+key);
            return Arrays.asList();
        }

    }
    /**
     * 删除缓存
     */
    private void deleteWinningRecodes(String key) {
        try{
            if(redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)) {
                redisUtil.del(WINNING_RECORDS_PREFIX+key);
            }
        }catch (Exception e){
            log.error("删除缓存中奖记录异常：key: {}", WINNING_RECORDS_PREFIX+key);
        }
    }
    /**
     * 根据活动id 或 奖品id 删除中奖记录
     * @param activityId
     * @param prizeId
     */
    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(null == activityId ) {
            log.warn("要删除中奖记录活动id为空");
        }
        //删除winner_record表
        winningRecordMapper.deleteRecords(activityId,prizeId);
        //删除缓存(奖品维度，活动维度)
        if(null != prizeId) {
            deleteWinningRecodes(activityId+"_"+prizeId);
        }
        deleteWinningRecodes(String.valueOf(activityId));
    }
    /**
     * 获取中奖记录
     * @param param
     * @return
     */
    @Override
    public List<WinningRecordsDTO> getRecords(WinningRecordsParam param) {
        //查询Redis
        String key = null == param.getPrizeId()
                ? String.valueOf(param.getActivityId())
                : param.getActivityId()+"_"+param.getPrizeId();
        List<WinningRecodesDO> winningRecords = getWinningRecords(key);

        if(!CollectionUtils.isEmpty(winningRecords)) {
            return convertToWinningRecordsDTOList(winningRecords);
        }
        //没查到查库
        winningRecords = winningRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(), param.getPrizeId());

        if(CollectionUtils.isEmpty(winningRecords)) {
            log.info("查询中奖记录为空 param: {}",
                    JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        //存放记录进Redis
        cacheWinningRecords(key,winningRecords,WINNING_RECORDS_TIMEOUT);
        return convertToWinningRecordsDTOList(winningRecords);
    }

    private List<WinningRecordsDTO> convertToWinningRecordsDTOList(List<WinningRecodesDO> winningRecords) {
        if(CollectionUtils.isEmpty(winningRecords)) {
            return Arrays.asList();
        }
        List<WinningRecordsDTO> winningRecordsDTOList = winningRecords.stream()
                .map(winningRecodesDO -> {
                    WinningRecordsDTO winningRecordsDTO = new WinningRecordsDTO();
                    winningRecordsDTO.setWinnerId(winningRecodesDO.getWinnerId());
                    winningRecordsDTO.setWinnerName(winningRecodesDO.getWinnerName());
                    winningRecordsDTO.setPrizeName(winningRecodesDO.getPrizeName());
                    winningRecordsDTO.setPrizeTier(ActivityPrizeTiresEnum.forName(winningRecodesDO.getPrizeTier()));
                    winningRecordsDTO.setWinningTime(winningRecodesDO.getWinningTime());
                    return winningRecordsDTO;
                }).collect(Collectors.toList());
        return winningRecordsDTOList;

    }


}
